<body></body>

<script type="module">
	// 渲染器
	const renderer = createRenderer({
		createElement: (tag) => document.createElement(tag),
		setElementText: (el, text) => (el.textContent = text),
		insert: (el, parent, anchor = null) => parent.insertBefore(el, anchor)
	})

	function createRenderer(optios) {
		const { createElement, setElementText, insert } = optios

		function shouldSetAsProps(el, key, value) {
			if (key === 'form' && el.tagName === 'INPUT') return false //特殊处理
			return key in el
		}

		// patchProps：选择合适的设置方式 用于更新元素的属性
		// prevValue：表示该属性在上一次渲染或更新时的值，即属性之前的值。
		// nextValue：表示该属性在当前渲染或更新时的新值，即属性现在的值。

		function patchProps(el, key, prevValue, nextValue) {
			if (key.startsWith('on')) {
				let invoker = el._vei //获取元素伪造事件处理函数
				const name = key.slice(2).toLowerCase() //获取元素事件
				if (nextValue) {
					if (!invoker) {
						invoker = el._vei = (e) => invoker.value(e) // 没有invoker将 invoker 缓存到 el._vei , 当伪造的事件处理函数执行，会执行真正的事件处理函数
						invoker.value = nextValue // 将真正对的 事件处理函数执行赋值给 invoker.value
						el.addEventListener(name, invoker)
					} else {
						invoker.value = nextValue // 如果 invoker 存在，意味着更新，并且只需要 invoker.value 的值即可
					}
				} else if (invoker) {
					// 新的事绑定函数不存在，且之前绑定的 invoker 存在，则移除绑定
					el.addEventListener(name, invoker)
				}
			}

			//其他判断
			else if (key === 'class') {
				el.className = '' // 调用封装函数处理class
			} else if (shouldSetAsProps(el, key, nextValue)) {
				const type = typeof el[key] //  获取该 DOM 属性值 的类型
				if (type === 'boolean' && nextValue === '') {
					el[key] = true // 如果是dom使用标准是布尔值，并且当前nextValue是空，将值矫正为true
				} else {
					el[key] = nextValue
				}
			} else {
				el.setAttribute(key, nextValue)
			}
		}

		// 挂载
		function mountElement(vnode, container) {
			const el = (vnode.el = createElement(vnode.type)) // 让 vnode.el 存一份 真实 DOM 元素 对的指针 ，用于回收元素

			if (typeof vnode.children === 'string') {
				setElementText(el, vnode.children) // 元素是字符串，直接设置文本
			} else if (Array.isArray(vnode.children)) {
				vnode.children.forEach((child) => {
					patch(null, child, el) // 子节点是数组，遍历child调用patch挂载他们
				})
			}

			if (vnode.props) {
				for (const key in vnode.props) {
					patchProps(el, key, null, vnode.props[key]) // 区分更新
				}
			}
			insert(el, container)
		}

		// 卸载操作
		function unmont(vnode) {
			const el = vnode.el // 使用前面存旧的元素(代码33行)
			const parent = el.parentNode //获取父元素去移除元素
			if (parent) parent.removeChild(el)
		}

		// 分支
		function render(vnode, container) {
			// console.log(vnode, container) // vnode虚拟dom，container挂载点(暂是document.body）
			if (vnode) {
				patch(container._vnode, vnode, container)
			} else {
				if (container._vnode) {
					unmont(container._vnode) // 调用unmont函数卸载 vnode,除了移除元素后面还有绑定事件指令，要卸载
				}
			}
			container._vnode = vnode // container.vnode代表旧的vnode
		}

		// patch：n1旧的，n2新的，container挂载点
		function patch(n1, n2, container) {
			// 1、旧的 vnode 已存在，而且newVnode和vnode冲突
			if (n1 && n1.type !== n2.type) {
				unmont(n1) // 卸载oldVnode
				n1 = null
			}
			// 2、证明 n1 和 n2 所描述的内容相同
			const { type } = n2
			// 如果 n2.type 的值是字符串类型（比如：'input' 'button'）其他（组件vnode）
			if (typeof type === 'string') {
				if (!n1) {
					mountElement(n2, container) //挂载newVnode
				} else {
					console.log('n1存在, 意味需要打补丁，暂时没做到')
				}
			} else if (typeof type === 'object') {
				// 如果 n2.type 属于对象类型，那它描述的是组件
			} else if (type === '其他') {
				// 处理其他类型的 vnodoe
			}
		}

		return { render }
	}

	const vnode = {
		type: 'button',
		props: {
			id: 'log',
			onClick: () => alert('nihk')
		},
		children: 'txt'
	}

	renderer.render(vnode, document.body) // 这是渲染，传递虚拟dom，挂载点
	// 封装 unmont 函数 处理 移除元素，后面还要处理 指令、生命周期 等等
</script>
